<template>
    <a-modal v-model:open="data.open" :maskClosable="false" centered width="1200px" title="自定义函数编辑" cancelText="取消"
        okText="保存" @cancel="handleCancel" @ok="handleOk" wrap-class-name="custom-modal-wrap scriptEditor">

        <div class="script-editor-wrap">

            <span class="cureent-info" v-if="data.selectItem">
                <a-tooltip placement="right">
                    <template #title>
                        <span>当前组件：</span><br>
                        组件名称：{{ getCurrentName() }}<br>
                        组件ID：{{ data.selectItem.id }}<br>
                    </template>
                    <span class="cureent-item">{{ getCurrentName() }}</span>
                </a-tooltip>
                &nbsp;> {{ data.currentEvent.type }}事件（{{data.editType == 'add' ? '新增' : '编辑'}}）
            </span>

            <div class="event-wrap">
                <div class="event-sidebar">
                    <span class="event-sub-title">函数列表</span>
                    <ul class="event-list" ref="eventList" @scroll="onScroll"
                        :class="{ 'isScrolling': data.isScrolling }">
                        <li class="event-item" :class="{active:item.active}" v-for="(item,index) in funcs"
                            @click="handleClickEvent(item)" :title="item.name">
                            <span class="event-name">{{item.name}}</span>
                            <template v-if="getBoundThisMehodComponent(item.name).length > 0">
                                <a-tooltip placement="right" overlayClassName="bound-event-tooltip">
                                    <template #title>
                                        <template v-if="getBoundThisMehodComponent(item.name).length > 0">
                                            <span class="bound-event-tooltip-title mb5">已绑定该函数的组件：</span>
                                            <div class="bound-component-item"
                                                v-for="(cmpItem,index) in getBoundThisMehodComponent(item.name)">
                                                <span>组件：{{ cmpItem.component.name }}{{ cmpItem.component.id }}</span>
                                                <span>事件：{{ cmpItem.eventDesc }}{{ cmpItem.eventName }}</span>
                                            </div>
                                        </template>
                                        <template v-else>
                                            <span class="bound-event-tooltip-title">该函数未绑定任何组件</span>
                                        </template>
                                    </template>
                                    <span class="event-bound-handler">
                                        <a-badge :count="getBoundThisMehodComponent(item.name).length" class="bound-event-badge"
                                            color="#3e8bf2"></a-badge>
                                    </span>
                                </a-tooltip>
                            </template>
                        </li>
                    </ul>
                </div>
                <div class="event-content">
                    <a-alert v-if="storage.scriptEditorError" type="error" banner>
                        <template #message>
                            当前函数编辑器存在错误，请检查您的代码
                            <a-tooltip placement="bottom">
                                <template #title>
                                    <div class="error-tips">
                                        <p>1. 检查拼写错误</p>
                                        <p>2. 检查函数名是否重复</p>
                                        <p>3. 检查是否存在语法错误</p>
                                        <p>4. 检查函数是否正确向外暴露</p>
                                    </div>
                                </template>
                                <ExclamationCircleOutlined class="tips-icon" style="margin-left:2px" />
                            </a-tooltip>
                        </template>
                    </a-alert>
                    <span class="event-sub-title">函数编辑</span>
                    <div class="event-editor" :class="{ 'hasError': storage.scriptEditorError }">
                        <monaco-editor v-model="generateData.script" valueFormat="javascript"
                            :minimap="true"></monaco-editor>
                    </div>
                </div>
            </div>
        </div>
        <template #footer>
            <div class="script-editor-footer">
                <a-button type="primary" @click="handleUpdate">更新函数</a-button>
                <a-button @click="handleCancel">关闭</a-button>
            </div>
        </template>

    </a-modal>
</template>

<script setup>

import { ref, reactive, defineExpose } from 'vue'
import monacoEditor from '../../common/extens/monacoEditor/monacoEditor.vue'
import { generateData, funcs, setDynamicsMethods, getBoundThisMehodComponent, storage } from '../../pageManager.js'
import { message } from 'ant-design-vue';
import { revoke } from '../../common/util/revoke.js';

const data = reactive({
    open: false,
    currentEvent: {},
    selectedMethod: {},
    prevMethodName: "",
    editType: "add",
    selectItem: null,
    isScrolling: false,
})

const eventList = ref(null);

/**
 * @method
 * @desc 打开弹窗
 */
const open = (selectItem, currentEvent, type, editMethodName) => {


    // console.log("selectItem::", selectItem);

    // 每次打开时，重新设置一下动态方法
    setDynamicsMethods(generateData.script);

    data.selectItem = selectItem;
    data.editType = type;
    data.open = true
    // 当前需要设置的事件
    data.currentEvent = currentEvent

    if (type == 'edit') {
        data.prevMethodName = editMethodName;
        setFuncsTrueActive(editMethodName)
    } else if (type == 'add') {
        data.prevMethodName = "";
        setFuncsFalseActive();
    }

}

/**
 * 滚动时间加效果
 */
const onScroll = (e) => {
    // console.log("正在滚动：", e);
    data.isScrolling = eventList.value.scrollTop !== 0;
}

/**
 * @method
 * @desc 获取当前组件名称
 */
const getCurrentName = () => {
    if (data.selectItem.label == '页面')
        return '页面';
    else
        return data.selectItem.name
}

/**
 * @method
 * @desc 点击函数列表
 */
const handleClickEvent = (methodItem) => {

    let curEvent = data.currentEvent;

    if (methodItem.name == data.prevMethodName) {
        // message.warning("此事件已绑定该函数，不能重复绑定")
        return false;
    }

    if (data.selectItem.event) {
        // 如果当前组件存在此事件的函数绑定
        if (data.selectItem.event[curEvent.type]) {
            if (isExistsBindMethod(methodItem)) {
                message.warning("此事件已绑定该函数，不能重复绑定")
                return false;
            }
        }
    }

    // 设置点击函数的选中状态
    setFuncsTrueActive(methodItem.name)

    data.selectedMethod = {
        methodName: methodItem.name
    }

    if (data.editType == 'add') {
        bindEventMethod(methodItem);
    } else if (data.editType == 'edit') {
        let curMethodList = data.selectItem.event[curEvent.type];
        for (let i = 0; i < curMethodList.length; i++) {
            let item = curMethodList[i];
            if (item.methodName == data.prevMethodName) {
                // 修改方法名
                item.methodName = data.selectedMethod.methodName;
                // 将上一次设置的方法名称改为现在的方法名称
                data.prevMethodName = data.selectedMethod.methodName;
            }
        }
    }
    // console.log("当前::", data.selectItem);
    if (data.selectItem.label == "页面") {
        revoke.add(generateData.data, `往【页面】【${curEvent.type}】事件绑定【${methodItem.name}】函数`)
    } else {
        revoke.add(generateData.data, `往【${data.selectItem.name}】组件【${curEvent.type}】事件绑定【${methodItem.name}】函数`)
    }


}

/**
 * @method
 * @param {Object} methodItem
 * @desc 绑定事件函数
 */
const bindEventMethod = (methodItem) => {
    let curEvent = data.currentEvent;
    let curItemEvent = {
        name: curEvent.name,
        methodName: data.selectedMethod.methodName
    }
    // console.log("需要给组件绑定的事件::", curItemEvent);

    // 当前组件存在事件绑定的对象
    if (data.selectItem.event) {

        // 如果当前组件存在此事件的函数绑定
        if (data.selectItem.event[curEvent.type]) {
            if (!isExistsBindMethod(methodItem)) {
                data.selectItem.event[curEvent.type].push(curItemEvent);
            }
        } else { // 如果不存在 则初始化此事件的函数列表数组再赋值
            data.selectItem.event[curEvent.type] = [];
            data.selectItem.event[curEvent.type].push(curItemEvent);
        }
    } else { // 如果不存在事件绑定对象则初始化事件对象和函数列表数组再赋值
        data.selectItem.event = {};
        data.selectItem.event[curEvent.type] = [];
        data.selectItem.event[curEvent.type].push(curItemEvent);
    }

    // 赋值 上一次设置的函数名
    data.prevMethodName = curItemEvent.methodName

    // 改变状态
    data.editType = 'edit';
}


/**
 * @method
 * @desc 判断函数是否已经绑定
 * @returns {Boolean} true 已经绑定 false 未绑定
 */
const isExistsBindMethod = (methodItem) => {
    // console.log("当前需要判断的函数::", methodItem)
    let curEvent = data.currentEvent;
    // console.log("当前组件的事件：", data.selectItem.event[curEvent.type])

    let curMethodList = data.selectItem.event[curEvent.type];

    for (let i = 0; i < curMethodList.length; i++) {
        let item = curMethodList[i];
        if (item.methodName == methodItem.name) {
            // 当前函数已经绑定
            return true
        }
    }
    return false;
}


/**
 * @method
 * @desc 更新函数
 */
const handleUpdate = () => {
    setDynamicsMethods(generateData.script, (success) => {
        setFuncsTrueActive(data.selectedMethod.methodName);
        revoke.addHistory(generateData.data, `更新动态函数${success ? '成功' : '失败'}`);
        if (success) {
            message.success("更新动态函数成功")
        } else {
            message.error("更新动态函数失败")
        }
    });
}

/**
 * @method
 * @param {String} methodName 方法名
 * @desc 设置函数列表选中状态
 */
const setFuncsTrueActive = (methodName) => {
    for (let i = 0; i < funcs.value.length; i++) {
        let funcsItem = funcs.value[i];
        if (funcsItem.name == methodName) {
            funcsItem.active = true;
        } else {
            funcsItem.active = false;
        }
    }
}

/**
 * @method
 * @desc 设置函数列表非选中状态
 */
const setFuncsFalseActive = () => {
    for (let i = 0; i < funcs.value.length; i++) {
        let funcsItem = funcs.value[i];
        funcsItem.active = false;
    }
}


/**
 * @method
 * @desc 点击保存
 */
const handleOk = () => {
    handleCancel();
}

/**
 * @method
 * @desc 关闭弹窗时的回调
 */
const handleCancel = () => {
    data.selectItem = null;
    data.open = false;
}

defineExpose({
    open
})

</script>

<style lang="less" scoped>
.script-editor-wrap {
    height: 100%;
    background-color: #f5f5f5;

    .cureent-info {
        padding: 5px 10px 0;
        display: flex;
        font-size: 12px;
        color: #959595;

        .cureent-item {
            cursor: pointer;
        }
    }

    .event-wrap {
        width: 100%;
        height: calc(100% - 24px);
        display: flex;
        padding: 5px 8px 8px;

        .event-sub-title {
            font-size: 14px;
            color: #000000d9;
            padding: 5px 10px 8px;
            display: block;
        }

        .event-sidebar {
            width: 220px;
            height: 100%;
            border: 1px solid var(--borderColor);
            margin-right: 8px;
            background-color: #fff;


            .event-list {
                margin: 0;
                padding:0 8px 5px;
                height: calc(100% - 35px);
                overflow: auto;

                &.isScrolling {
                    box-shadow: #dddddd 0 6px 6px -6px inset;
                }

                .event-item {
                    border: 1px solid #ccc;
                    border-radius: 3px;
                    margin-bottom: 5px;
                    padding: 5px 10px;
                    cursor: pointer;
                    display: flex;
                    justify-content: space-between;
                    align-items: center;

                    .event-name {
                        overflow: hidden;
                        white-space: nowrap;
                        text-overflow: ellipsis;
                    }

                    &:hover {
                        background-color: #f5f5f5;
                    }

                    &.active {
                        border: 1px solid rgba(62, 140, 242, 0.5);
                        background-color: rgba(62, 140, 242, 0.1);
                    }
                }
            }
        }

        .event-content {
            width: calc(100% - 220px);
            height: 100%;
            background-color: #fff;
            border: 1px solid var(--borderColor);

            .event-editor {
                height: calc(100% - 35px);

                &.hasError {
                    height: calc(100% - 35px - 38px);
                }
            }
        }
    }
}

.script-editor-footer {
    display: flex;
    justify-content: space-between;
}

.bound-event-tooltip-title {
    font-size: 12px;
    display: block;
}

.mb5 {
    margin-bottom: 5px;
}

.bound-component-item {
    background-color: #fff;
    color: #333;
    font-size: 12px;
    padding: 5px;
    border-radius: 4px;
    margin-bottom: 5px;

    span {
        display: block;
        font-weight: bold;
    }
}
.error-tips {
    padding: 5px;
    p {
        margin-bottom: 0;
    }
}

</style>